/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 **************************************************************************
 * MIFSS  - content storage system
 * 
 *
 * @uthors: uros.kristan@gmail.com (Uroš Kristan ) Urosk.NET
 *         jernej.svigelj@gmail.com (Jernej Švigelj) 
 */
package net.monare.mifss.pools;

import java.io.File;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Set;

import net.monare.mifss.configurations.pojo.ContentMetaDataDef;
import net.monare.mifss.configurations.pojo.ContentPoolDef;
import net.monare.mifss.configurations.pojo.Property;
import net.monare.mifss.exceptions.ContentPoolHandlerException;
import net.monare.mifss.lib.FileHelper;
import net.monare.mifss.workers.interfaces.ContentPoolHandler;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

public class FsContentPoolImpl implements ContentPoolHandler {

	private static Logger logger = Logger.getLogger(FsContentPoolImpl.class);

	private static final int hexPaddingLength = 10;

	private ContentPoolDef contentPoolDef;

	private String storageFolder;

	public FsContentPoolImpl() {

	}

	@Override
	public void checkPool() throws ContentPoolHandlerException {

		if (storageFolder == null) {
			String errMessage = "Poll is not initialized. Storage folder setting is null ...";
			logger.error(errMessage);
			throw new ContentPoolHandlerException(errMessage);
		}

		File targetFolder = new File(storageFolder);

		if (!targetFolder.exists()) {
			String errMessage = "Storage folder does not exist ...";
			logger.error(errMessage);
			throw new ContentPoolHandlerException(errMessage);
		}

		if (!targetFolder.isDirectory()) {
			String errMessage = "Storage folder setting in pool defintion points to file ...";
			logger.error(errMessage);
			throw new ContentPoolHandlerException(errMessage);
		}

	}

	@Override
	public void createPool() throws ContentPoolHandlerException {

		// Not needed in file system pool. Folders are created automatically via
		// FileUtils.forceMkdir method during content write.
		File targetFolder = new File(storageFolder);
		try {
			FileUtils.forceMkdir(targetFolder);
		} catch (IOException e) {
			String errMessage = "Storage folder create failed..." + e.getMessage();
			logger.error(errMessage);
			throw new ContentPoolHandlerException(errMessage, e);
		}

	}

	@Override
	public ContentPoolDef getContentPoolDef() {
		return contentPoolDef;

	}

	/**
	 * returns calculated absolute path to file
	 * 
	 * @param contentMetaDataDef
	 * @return
	 */
	private String calculatePathToContent(ContentMetaDataDef contentMetaDataDef) {

		String hexPath = getHexStorePath(contentMetaDataDef.getIdContent());
		String extension = contentMetaDataDef.getExtension();

		return storageFolder + File.separator + hexPath + "." + extension;

	}

	@Override
	public void getFile(File outfile, ContentMetaDataDef contentMetaDataDef) throws ContentPoolHandlerException {

		File srcFile = new File(calculatePathToContent(contentMetaDataDef));

		try {

			FileUtils.copyFile(srcFile, outfile);

		} catch (IOException e) {

			String errMessage = "Error occured when tried to copy " + srcFile.getPath() + " to " + outfile.getPath() + " . "
					+ e.getMessage();
			logger.error(errMessage, e);
			throw new ContentPoolHandlerException(errMessage, e);

		}
	}

	/**
	 * Creates left padded string from int to hex, splited in folder from 0-255
	 * in hex
	 * 
	 * @param id
	 * @return
	 */
	public String getHexStorePath(long id) {

		String s = Long.toHexString(id);
		s = s.toLowerCase();

		s = StringUtils.leftPad(s, hexPaddingLength, "0");

		String a = "";
		for (int i = 0; i < hexPaddingLength; i = i + 2) {
			a = a + s.substring(i, i + 2) + File.separator;
		}

		return a.substring(0, a.length() - 1);
	}

	@Override
	public void initializePool(ContentPoolDef contentPoolDef) throws ContentPoolHandlerException {
		storageFolder = (String) contentPoolDef.getPropertyValue("storage_folder");

	}

	@Override
	public void putFile(File file, ContentMetaDataDef contentMetaDataDef) throws ContentPoolHandlerException {

		File trg = new File(calculatePathToContent(contentMetaDataDef));

		try {

			// FileUtils.forceMkdir(new File(trg.getParent()));
			// FileUtils.copyFile(file, trg);
			Files.createDirectories(Paths.get(trg.getParent()));
			FileHelper.copyFile(file, trg);
			

		} catch (IOException e) {

			String errMessage = "Error occured when tried to copy " + file.getPath() + " to " + trg.getPath() + " . " + e.getMessage();
			logger.error(errMessage, e);
			throw new ContentPoolHandlerException(errMessage, e);

		}

	}

	@Override
	public void setContentPoolDef(ContentPoolDef contentPoolDef) {
		this.contentPoolDef = contentPoolDef;

	}

	@Override
	public void wipeCompleteData(ContentPoolDef contentPoolDef) throws ContentPoolHandlerException {

		String folderToRemove = null;

		try {

			logger.info(" Wiping complete pool folder from " + contentPoolDef);

			folderToRemove = (String) contentPoolDef.getPropertyValue("storage_folder");
			FileUtils.deleteDirectory(new File(folderToRemove));

		} catch (IOException e) {

			String errMessage = "Error occured while deleting folder " + folderToRemove + " . " + e.getMessage();
			logger.error(errMessage, e);
			throw new ContentPoolHandlerException(errMessage, e);

		}

	}

    @Override
    public Set<Property> getDefaultProperties() {

        // TODO
        return null;
    }

    @Override
	public void deleteFile(ContentMetaDataDef contentMetaDataDef) throws ContentPoolHandlerException {

		File targetFile = new File(calculatePathToContent(contentMetaDataDef));
		boolean deleted = FileUtils.deleteQuietly(targetFile);

		if (!deleted) {

			String errMessage = "File " + targetFile.getPath() + " not deleted!";
			logger.error(errMessage);
			throw new ContentPoolHandlerException(errMessage);

		}

	}

}
